<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="XvrCz" id="XvrCz"><span data-lake-id="u94f6ad3b" id="u94f6ad3b">典型回答</span></h1>
  <p data-lake-id="ue3384254" id="ue3384254"><br></p>
  <p data-lake-id="uae078392" id="uae078392"><span data-lake-id="u66d69be2" id="u66d69be2">有区别，而且区别很大。</span></p>
  <p data-lake-id="u43df2a4a" id="u43df2a4a"><span data-lake-id="u0f685c2d" id="u0f685c2d">​</span><br></p>
  <p data-lake-id="u006ba2e8" id="u006ba2e8"><span data-lake-id="u692087e0" id="u692087e0">因为double是不精确的，所以使用一个不精确的数字来创建BigDeciaml，得到的数字也是不精确的。如0.1这个数字，double只能表示他的近似值。</span></p>
  <p data-lake-id="ub4ad62fe" id="ub4ad62fe"><br></p>
  <p data-lake-id="u755dbd23" id="u755dbd23"><span data-lake-id="u806e50f9" id="u806e50f9">所以，</span><strong><span data-lake-id="u1d66de9c" id="u1d66de9c">当我们使用new BigDecimal(0.1)创建一个BigDecimal 的时候，其实创建出来的值并不是正好等于0.1的。</span></strong></p>
  <p data-lake-id="ub0914da4" id="ub0914da4"><br></p>
  <p data-lake-id="uadc4185f" id="uadc4185f"><span data-lake-id="ub39fda39" id="ub39fda39">而是0.1000000000000000055511151231257827021181583404541015625。这是因为double自身表示的只是一个近似值。</span></p>
  <p data-lake-id="u877346ea" id="u877346ea"><span data-lake-id="u30ec5b6f" id="u30ec5b6f">​</span><br></p>
  <p data-lake-id="uf97de95f" id="uf97de95f"><span data-lake-id="uf5907a98" id="uf5907a98">而对于BigDecimal(String) ，当我们使用new BigDecimal("0.1")创建一个BigDecimal 的时候，其实创建出来的值正好就是等于0.1的。</span></p>
  <p data-lake-id="uddc21ecc" id="uddc21ecc"><br></p>
  <p data-lake-id="ud99f5fee" id="ud99f5fee"><span data-lake-id="u5fe04151" id="u5fe04151">那么他的标度也就是1</span></p>
  <p data-lake-id="u687756bb" id="u687756bb"><span data-lake-id="u5be44baf" id="u5be44baf">​</span><br></p>
  <h1 data-lake-id="NTktI" id="NTktI"><span data-lake-id="u68e4a330" id="u68e4a330">扩展知识</span></h1>
  <p data-lake-id="u39637001" id="u39637001"><span data-lake-id="ub9623ff3" id="ub9623ff3">在《阿里巴巴Java开发手册》中有一条建议，或者说是要求：</span></p>
  <p data-lake-id="u882c477f" id="u882c477f"><br></p>
  <p data-lake-id="uac8b907c" id="uac8b907c"><img src="http://www.hollischuang.com/wp-content/uploads/2021/01/16119907257353.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_41%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u3079ebfa" id="u3079ebfa">​</span></p>
  <p data-lake-id="u50d713ea" id="u50d713ea"><br></p>
  <h3 data-lake-id="b6917330" id="b6917330"><span data-lake-id="uaaf5842d" id="uaaf5842d">BigDecimal如何精确计数？</span></h3>
  <p data-lake-id="u3274c7f9" id="u3274c7f9"><br></p>
  <p data-lake-id="u8af7b3d1" id="u8af7b3d1"><span data-lake-id="u8095b7b2" id="u8095b7b2">如果大家看过BigDecimal的源码，其实可以发现，</span><strong><span data-lake-id="uc5f56127" id="uc5f56127">实际上一个BigDecimal是通过一个"无标度值"和一个"标度"来表示一个数的。</span></strong></p>
  <p data-lake-id="u346c37c1" id="u346c37c1"><br></p>
  <p data-lake-id="u069d8028" id="u069d8028"><span data-lake-id="u517befbe" id="u517befbe">在BigDecimal中，标度是通过scale字段来表示的。</span></p>
  <p data-lake-id="u40c39b9c" id="u40c39b9c"><br></p>
  <p data-lake-id="u0157cba2" id="u0157cba2"><span data-lake-id="u597b6502" id="u597b6502">而无标度值的表示比较复杂。当unscaled value超过阈值(默认为Long.MAX_VALUE)时采用intVal字段存储unscaled value，intCompact字段存储Long.MIN_VALUE，否则对unscaled value进行压缩存储到long型的intCompact字段用于后续计算，intVal为空。</span></p>
  <p data-lake-id="u9ad0e900" id="u9ad0e900"><br></p>
  <p data-lake-id="u730c1e52" id="u730c1e52"><span data-lake-id="ua80f4a18" id="ua80f4a18">涉及到的字段就是这几个：</span></p>
  <p data-lake-id="ubaa0e3a7" id="ubaa0e3a7"><br></p>
  <pre lang="java"><code>
public class BigDecimal extends Number implements Comparable&lt;BigDecimal&gt; {
    private final BigInteger intVal;
    private final int scale; 
    private final transient long intCompact;
}
</code></pre>
  <p data-lake-id="u68712d21" id="u68712d21"><br></p>
  <p data-lake-id="u6c687f5a" id="u6c687f5a"><span data-lake-id="ud494c23a" id="ud494c23a">关于无标度值的压缩机制大家了解即可，不是本文的重点，大家只需要知道BigDecimal主要是通过一个无标度值和标度来表示的就行了。</span></p>
  <p data-lake-id="u750c789b" id="u750c789b"><br></p>
  <p data-lake-id="ue812a96f" id="ue812a96f"><strong><span data-lake-id="uf85ed8b6" id="uf85ed8b6">那么标度到底是什么呢？</span></strong></p>
  <p data-lake-id="u232f1143" id="u232f1143"><br></p>
  <p data-lake-id="ucd35eca1" id="ucd35eca1"><span data-lake-id="u53ce4009" id="u53ce4009">除了scale这个字段，在BigDecimal中还提供了scale()方法，用来返回这个BigDecimal的标度。</span></p>
  <p data-lake-id="u48ec0a5b" id="u48ec0a5b"><br></p>
  <pre lang="java"><code>
/**
 * Returns the &lt;i&gt;scale&lt;/i&gt; of this {@code BigDecimal}.  If zero
 * or positive, the scale is the number of digits to the right of
 * the decimal point.  If negative, the unscaled value of the
 * number is multiplied by ten to the power of the negation of the
 * scale.  For example, a scale of {@code -3} means the unscaled
 * value is multiplied by 1000.
 *
 * @return the scale of this {@code BigDecimal}.
 */
public int scale() {
    return scale;
}
</code></pre>
  <p data-lake-id="u82acc718" id="u82acc718"><br></p>
  <p data-lake-id="ufabebca6" id="ufabebca6"><span data-lake-id="uf5d9972c" id="uf5d9972c">那么，scale到底表示的是什么，其实上面的注释已经说的很清楚了：</span></p>
  <p data-lake-id="u6b807ee7" id="u6b807ee7"><br></p>
  <blockquote data-lake-id="u05306ad0" id="u05306ad0">
   <p data-lake-id="u60d0271b" id="u60d0271b"><span data-lake-id="u90688a4c" id="u90688a4c">如果scale为零或正值，则该值表示这个数字小数点右侧的位数。如果scale为负数，则该数字的无标度值需要乘以10的该负数的绝对值的幂。例如，scale为-3，则这个数需要乘1000，即在末尾有3个0。</span></p>
  </blockquote>
  <p data-lake-id="u559056a0" id="u559056a0"><br></p>
  <p data-lake-id="u63b42b9f" id="u63b42b9f"><span data-lake-id="u4f2dabb6" id="u4f2dabb6">如123.123，那么如果使用BigDecimal表示，那么他的无标度值为123123，他的标度为3。</span></p>
  <p data-lake-id="uc020be09" id="uc020be09"><br></p>
  <p data-lake-id="u93ac5c71" id="u93ac5c71"><strong><span data-lake-id="u42b12626" id="u42b12626">而二进制无法表示的0.1，使用BigDecimal就可以表示了，及通过无标度值1和标度1来表示。</span></strong></p>
  <p data-lake-id="ud2aff048" id="ud2aff048"><br></p>
  <p data-lake-id="uee9a5e42" id="uee9a5e42"><span data-lake-id="uc5a1b347" id="uc5a1b347">我们都知道，想要创建一个对象，需要使用该类的构造方法，在BigDecimal中一共有以下4个构造方法：</span></p>
  <p data-lake-id="u7bbd8bf6" id="u7bbd8bf6"><br></p>
  <pre lang="java"><code>
BigDecimal(int)
BigDecimal(double) 
BigDecimal(long) 
BigDecimal(String)
</code></pre>
  <p data-lake-id="ud482f317" id="ud482f317"><br></p>
  <p data-lake-id="u66458441" id="u66458441"><span data-lake-id="ud8a72427" id="ud8a72427">以上四个方法，创建出来的的BigDecimal的标度（scale）是不同的。</span></p>
  <p data-lake-id="ua4835412" id="ua4835412"><br></p>
  <p data-lake-id="u73e2e965" id="u73e2e965"><span data-lake-id="u1dff5063" id="u1dff5063">其中 BigDecimal(int)和BigDecimal(long) 比较简单，因为都是整数，所以他们的标度都是0。</span></p>
  <p data-lake-id="ud81e1b03" id="ud81e1b03"><br></p>
  <p data-lake-id="u5efd3c62" id="u5efd3c62"><span data-lake-id="u2ff1f862" id="u2ff1f862">而BigDecimal(double) 和BigDecimal(String)的标度就有很多学问了。</span></p>
  <p data-lake-id="u03afb980" id="u03afb980"><br></p>
  <h3 data-lake-id="b5467c0f" id="b5467c0f"><span data-lake-id="ud2cfd87f" id="ud2cfd87f">BigDecimal(double)有什么问题</span></h3>
  <p data-lake-id="ub3e1e398" id="ub3e1e398"><br></p>
  <p data-lake-id="u2f15040c" id="u2f15040c"><span data-lake-id="uaa84b360" id="uaa84b360">BigDecimal中提供了一个通过double创建BigDecimal的方法——BigDecimal(double) ，但是，同时也给我们留了一个坑！</span></p>
  <p data-lake-id="u94cbe7e3" id="u94cbe7e3"><br></p>
  <p data-lake-id="u9bc85772" id="u9bc85772"><span data-lake-id="u5f1684e1" id="u5f1684e1">因为我们知道，double表示的小数是不精确的，如0.1这个数字，double只能表示他的近似值。</span></p>
  <p data-lake-id="u7aec6f55" id="u7aec6f55"><br></p>
  <p data-lake-id="u21c181aa" id="u21c181aa"><span data-lake-id="u3d051bbf" id="u3d051bbf">所以，</span><strong><span data-lake-id="u43c65608" id="u43c65608">当我们使用new BigDecimal(0.1)创建一个BigDecimal 的时候，其实创建出来的值并不是正好等于0.1的。</span></strong></p>
  <p data-lake-id="u45890df8" id="u45890df8"><br></p>
  <p data-lake-id="uecfae5ec" id="uecfae5ec"><span data-lake-id="u07aacc1d" id="u07aacc1d">而是0.1000000000000000055511151231257827021181583404541015625。这是因为double自身表示的只是一个近似值。</span></p>
  <p data-lake-id="u35ee2853" id="u35ee2853"><br></p>
  <p data-lake-id="u14cdd087" id="u14cdd087"><img src="http://www.hollischuang.com/wp-content/uploads/2021/01/16119945021181.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_31%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="ubaaa48ff" id="ubaaa48ff">​</span></p>
  <p data-lake-id="u571db3cb" id="u571db3cb"><br></p>
  <p data-lake-id="u8e586903" id="u8e586903"><strong><span data-lake-id="ufbbf6b28" id="ufbbf6b28">所以，如果我们在代码中，使用BigDecimal(double) 来创建一个BigDecimal的话，那么是损失了精度的，这是极其严重的。</span></strong></p>
  <p data-lake-id="u82cc8bd5" id="u82cc8bd5"><br></p>
  <h3 data-lake-id="ec9fd24d" id="ec9fd24d"><span data-lake-id="ued1ca714" id="ued1ca714">使用BigDecimal(String)创建</span></h3>
  <p data-lake-id="udebc5b33" id="udebc5b33"><br></p>
  <p data-lake-id="u117db4ee" id="u117db4ee"><span data-lake-id="u090732be" id="u090732be">那么，该如何创建一个精确的BigDecimal来表示小数呢，答案是使用String创建。</span></p>
  <p data-lake-id="u43ec3521" id="u43ec3521"><br></p>
  <p data-lake-id="u165ff3b5" id="u165ff3b5"><span data-lake-id="ub93a9177" id="ub93a9177">而对于BigDecimal(String) ，当我们使用new BigDecimal("0.1")创建一个BigDecimal 的时候，其实创建出来的值正好就是等于0.1的。</span></p>
  <p data-lake-id="u986ca30f" id="u986ca30f"><br></p>
  <p data-lake-id="ucd3098a3" id="ucd3098a3"><span data-lake-id="ud0745fd3" id="ud0745fd3">那么他的标度也就是1。</span></p>
  <p data-lake-id="u6f3264f9" id="u6f3264f9"><br></p>
  <p data-lake-id="ub91198ce" id="ub91198ce"><span data-lake-id="uc90d0384" id="uc90d0384">但是需要注意的是，new BigDecimal("0.10000")和new BigDecimal("0.1")这两个数的标度分别是5和1，如果使用BigDecimal的equals方法比较，得到的结果是false。</span></p>
  <p data-lake-id="u6ecea450" id="u6ecea450"><br></p>
  <p data-lake-id="u7ffac89f" id="u7ffac89f"><span data-lake-id="u46796185" id="u46796185">那么，想要创建一个能精确的表示0.1的BigDecimal，请使用以下两种方式：</span></p>
  <p data-lake-id="u804dfa49" id="u804dfa49"><br></p>
  <pre lang="java"><code>
BigDecimal recommend1 = new BigDecimal("0.1");
BigDecimal recommend2 = BigDecimal.valueOf(0.1);
</code></pre>
  <p data-lake-id="uad047f23" id="uad047f23"><br></p>
  <p data-lake-id="ub7a6ecb4" id="ub7a6ecb4"><span data-lake-id="ud88fdf19" id="ud88fdf19">这里，留一个思考题，BigDecimal.valueOf()是调用Double.toString方法实现的，那么，既然double都是不精确的，BigDecimal.valueOf(0.1)怎么保证精确呢？</span></p>
  <p data-lake-id="uca5ae642" id="uca5ae642"><br></p>
  <h3 data-lake-id="25f9c7fa" id="25f9c7fa"><span data-lake-id="u533ae4cf" id="u533ae4cf">总结</span></h3>
  <p data-lake-id="u0424ee26" id="u0424ee26"><br></p>
  <p data-lake-id="u288bb817" id="u288bb817"><span data-lake-id="u5778feed" id="u5778feed">因为计算机采用二进制处理数据，但是很多小数，如0.1的二进制是一个无限循环小数，而这种数字在计算机中是无法精确表示的。</span></p>
  <p data-lake-id="u4d88fec4" id="u4d88fec4"><br></p>
  <p data-lake-id="u202e2dd0" id="u202e2dd0"><span data-lake-id="uf58f359a" id="uf58f359a">所以，人们采用了一种通过近似值的方式在计算机中表示，于是就有了单精度浮点数和双精度浮点数等。</span></p>
  <p data-lake-id="u79b7e9da" id="u79b7e9da"><br></p>
  <p data-lake-id="ubb9fd11e" id="ubb9fd11e"><span data-lake-id="u925fefe9" id="u925fefe9">所以，作为单精度浮点数的float和双精度浮点数的double，在表示小数的时候只是近似值，并不是真实值。</span></p>
  <p data-lake-id="ud82c3581" id="ud82c3581"><br></p>
  <p data-lake-id="u73d9ca2e" id="u73d9ca2e"><span data-lake-id="u6dfdb3c0" id="u6dfdb3c0">所以，当使用BigDecimal(Double)创建一个的时候，得到的BigDecimal是损失了精度的。</span></p>
  <p data-lake-id="u6dc980a5" id="u6dc980a5"><br></p>
  <p data-lake-id="u7f3d9bee" id="u7f3d9bee"><span data-lake-id="uab2d1aab" id="uab2d1aab">而使用一个损失了精度的数字进行计算，得到的结果也是不精确的。</span></p>
  <p data-lake-id="u31e89453" id="u31e89453"><br></p>
  <p data-lake-id="u4a322e81" id="u4a322e81"><span data-lake-id="ub4ab1657" id="ub4ab1657">想要避免这个问题，可以通过BigDecimal(String)的方式创建BigDecimal，这样的情况下，0.1就会被精确的表示出来。</span></p>
  <p data-lake-id="u6ad81630" id="u6ad81630"><br></p>
  <p data-lake-id="u10a84bf1" id="u10a84bf1"><span data-lake-id="u4f4c722c" id="u4f4c722c">其表现形式是一个无标度数值1，和一个标度1的组合。</span></p>
 </body>
</html>